const { init, parse } = require("es-module-lexer");
const path = require("path");
const MagicString = require("magic-string");
const { lexAcceptedHmrDeps } = require("../server/hmr");
function importAnalysis(config) {
  const { root } = config;
  let server;
  return {
    name: "importAnalysis",
    // 交给 server/index.js 调用，那里注入 server
    configureServer(_server) {
      server = _server;
    },
    //处理模块 1.找文件 2读内容 3 转换内容
    //假如说我们转的是src/main.js source= main.js的原始内容

    //1.找到源文件中第三方模块2.进行转换 vue=>deps/vue.js

    /**
     * 假如说我们转的是src/main.js source= main.js的原始内容
     * @param {*} source 此模块的内容
     * @param {*} id 此模块的绝对路径
     * @returns
     */
    async transform(source, id) {
      //moduleId
      debugger;
      await init; //等待解析器初始化完成
      //获取导入的模块
      let imports = parse(source)[0];
      //如果没有导入任何模块，可以直接返回
      if (!imports.length) {
        return source;
      }
      // 获取依赖图
      const { moduleGraph } = server;
      //通过导入方的模块的路径获取模块的节点
      const currentModule = moduleGraph.getModuleById(id); // id ==> main.js
      //此模块将要导入的子模块
      const importedUrls = new Set(); //renderModule.js
      //接收变更的依赖模块
      const acceptedUrls = new Set(); //renderModule.js
      const ms = new MagicString(source);
      //url= vue =>  /node_modules/.vite/deps/vue.js
      const normalizeUrl = async (url) => {
        //./renderModule.js

        //内部其实是调用插件容器的resolveId方法返回url的绝对路径

        // this 值得是 插件容器上下文
        const resolved = await this.resolve(url, id); // 返回 resolved=C:\aproject\webpack202208\vite50use\src\renderModule.js
        //
        if (resolved && resolved.id.startsWith(root)) {
          // C:/vite50use/src/main.js=>/src/main.js
          // 这步的操作，就是把代码一，改为 代码二
          // 代码一  C:/vite50use/node_modules/.vite50/deps/vue.js 根目录删除掉
          // 代码二  /node_modules/.vite50/deps/vue.js
          url = resolved.id.slice(root.length); // 截取根目录返回 /src/renderModule.js
        }
        await moduleGraph.ensureEntryFromUrl(url); //建立此导入的模块和模块节点的对应关系
        return url;
      };
      //重写路径
      for (let index = 0; index < imports.length; index++) {
        //n=specifier=vue
        const { s: start, e: end, n: specifier } = imports[index]; //renderModule.js
        const rawUrl = source.slice(start, end); //原始的引入地址  截取的是import.meta
        if (rawUrl === "import.meta") {
          //import.meta.hot.accept(['./renderModule.js']
          const prop = source.slice(end, end + 4);
          if (prop === ".hot") {
            if (source.slice(end + 4, end + 11) === ".accept") {
              //截取 import.meta.hot.accept(['./renderModule.js'], ([renderModule])，截取 ['./renderModule.js']这一段

              // 解析热更新依赖
              lexAcceptedHmrDeps(
                source,
                source.indexOf("(", end + 11) + 1,
                acceptedUrls //此处存放的是原始的路径 相对的，也可能绝对的，也可以第三方的
              );
            }
          }
        }
        if (specifier) {
          //./renderModule.js
          const normalizedUrl = await normalizeUrl(specifier);
          if (specifier !== normalizedUrl) {
            ms.overwrite(start, end, normalizedUrl);
          }
          //把解析后的导入的模块ID添加到importedUrls， 这里绝对路径
          importedUrls.add(normalizedUrl);
        }
      }
      // 这里存放的是格式化后，绝对的路径
      const normalizedAcceptedUrls = new Set();
      const toAbsoluteUrl = (url) => {
        // 这一步，我们找./renderModule.js的时候需要去main.js所在的目录里找相对路径
        return path.posix.resolve(path.posix.dirname(currentModule.url), url);
      };
      for (const { url, start, end } of acceptedUrls) {
        //./renderModule.js  resolveUrl是的根目录下面的
        const normalized = await normalizeUrl(toAbsoluteUrl(url));
        normalizedAcceptedUrls.add(normalized);
        // 重写文件路径
        ms.overwrite(start, end, JSON.stringify(normalized));
      }
      //更新模块的依赖信息
      await moduleGraph.updateModuleInfo(
        //
        currentModule,
        //
        importedUrls,
        //
        normalizedAcceptedUrls
      );
      console.log(importedUrls); //Set(1) { '/src/renderModule.js' }
      console.log(normalizedAcceptedUrls);
      return ms.toString();
    },
  };
}
module.exports = importAnalysis;
